package com.gitee.floyd.springme.video.repo

import com.gitee.floyd.springme.video.REDIS_CACHE_NAME_PLAY_INFO
import com.gitee.floyd.springme.video.REDIS_CACHE_NAME_VIDEO
import com.gitee.floyd.springme.video.repo.entity.Video
import com.gitee.floyd.springme.video.repo.entity.VideoPlayInfo
import org.slf4j.LoggerFactory
import org.springframework.cache.CacheManager
import org.springframework.cache.annotation.CacheEvict
import org.springframework.cache.annotation.CachePut
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Repository

@Repository
class VideoRelatedRepoImpl(cacheManager: CacheManager) : VideoRelatedRepo {

    private val logger = LoggerFactory.getLogger(this::class.java)

    private val videoPlayInfoCache = cacheManager.getCache(REDIS_CACHE_NAME_PLAY_INFO)!!

    private val videoDB = mutableMapOf<String, Any>()
    private val videoPlayInfoDB = mutableMapOf<String, Any>()

    override fun addVideo(video: Video): Boolean {
        videoDB[video.id] = video
        return true
    }

    @Cacheable(cacheNames = [REDIS_CACHE_NAME_VIDEO], key = "#id")
    override fun getVideo(id: String): Video {
        logger.info("通过数据库获取值")
        return videoDB[id] as Video
    }

    @CachePut(cacheNames = [REDIS_CACHE_NAME_VIDEO], key = "#video.id")
    override fun updateVideo(video: Video): Video {
        videoDB[video.id] = video
        return videoDB[video.id] as Video
    }

    @CacheEvict(cacheNames = [REDIS_CACHE_NAME_VIDEO], key = "#id")
    override fun deleteVideo(id: String): Video {
        return videoDB.remove(id) as Video
    }

    override fun addVideoPlayInfo(videoPlayInfo: VideoPlayInfo): Boolean {
        videoPlayInfoDB[videoPlayInfo.id] = videoPlayInfo
        return true
    }

    override fun listVideoPlayInfo(videoIds: List<String>): List<VideoPlayInfo> {
        // 读取对应关系
        val playInfoIds = selectIdByVideoIds(videoIds)
        // 先从缓存获取
        val infosInCache = playInfoIds.mapNotNull { videoPlayInfoCache.get(it, VideoPlayInfo::class.java) }
        val infoIdsInDB = playInfoIds.filterNot { id -> infosInCache.any { it.id == id } }
        // 再从数据库获取
        val infosInDB = videoPlayInfoDB.multiGet(infoIdsInDB).map { it as VideoPlayInfo }
        // 再写入缓存
        infosInDB.forEach { videoPlayInfoCache.put(it.id, it) }
        logger.info("通过缓存获取了${infosInCache.size}; 通过数据库获取了${infosInDB.size}")
        return infosInCache + infosInDB
    }

    @CachePut(value = [REDIS_CACHE_NAME_PLAY_INFO], key = "#videoPlayInfo.id")
    override fun updateVideoPlayInfo(videoPlayInfo: VideoPlayInfo): VideoPlayInfo {
        videoPlayInfoDB[videoPlayInfo.id] = videoPlayInfo
        return videoPlayInfoDB[videoPlayInfo.id] as VideoPlayInfo
    }

    @CacheEvict(cacheNames = [REDIS_CACHE_NAME_PLAY_INFO], key = "#id")
    override fun deleteVideoPlayInfo(id: String): VideoPlayInfo {
        return videoPlayInfoDB[id] as VideoPlayInfo
    }

    private fun <K, V> MutableMap<K, V>.multiGet(ids: List<K>): List<V> = ids.map { this[it]!! }

    // 模拟数据库关系获取
    private fun selectIdByVideoIds(videoIds: List<String>) = videoPlayInfoDB
        .filter { (it.value as VideoPlayInfo).videoId in videoIds }
        .map { it.key }

}
